import os, subprocess, sys, threading, time

""" Restarts the program given as 'main_func' if code of this programm has
changed. """

_modification_times = {}

def main(main_func, args=(), kwargs={}):
    if os.environ.get("RUN_MAIN") == "true":
        thread = threading.Thread(target=main_func, args=args, kwargs=kwargs)
        thread.setDaemon(True)
        thread.start()
        try:
            _exit_on_change()
        except KeyboardInterrupt:
            pass
    else:
        try:
            sys.exit(_start_subprocess())
        except KeyboardInterrupt:
            pass

def _exit_on_change():
    while True:
        if _code_changed():
            sys.exit(3)
        time.sleep(1)

def _code_changed():
    global _modification_times
    def get_filename(module):
        filename = getattr(module, '__file__', None)
        if filename:
            if filename.endswith('.pyc') or filename.endswith('.pyo'):
                filename = filename[:-1]
        return filename
    for filename in map(get_filename, sys.modules.values()):
        if filename is None:
            continue
        if not os.path.exists(filename):
            continue # File might be in an egg, so it can't be reloaded.
        modification_time = os.stat(filename).st_mtime
        if sys.platform == "win32":
            modification_time -= os.stat.st_ctime
        if filename not in _modification_times:
            _modification_times[filename] = modification_time
            continue
        if modification_time != _modification_times[filename]:
            _modification_times = {}
            return True
    return False

def _start_subprocess():
    while True:
        args = [sys.executable] + sys.argv
        if sys.platform == 'win32':
            args = ['"%s"' % arg for arg in args]
        new_environ = os.environ.copy()
        new_environ["RUN_MAIN"] = 'true'
        child = subprocess.Popen(args, env=new_environ)
        child.wait()
        exit_code = child.returncode
        if exit_code != 3:
            return exit_code
